// @ts-nocheck
import React, { Fragment, useEffect, useRef, useState } from "react";
import { Badge, Pagination, Select, Space, Table } from "antd";
import { PaginationProps } from "antd/lib/pagination";
import { ColumnProps, TableProps } from "antd/lib/table";
import { SelectProps } from "antd/lib/select";
import cls from "classnames";
import {
  observer,
  RecursionField,
  useField,
  useFieldSchema,
  useForm,
} from "@formily/react";
import { onFieldInputValueChange, onFieldValueChange } from "@formily/core";

import { FormPath, isArr, isBool } from "@formily/shared";
import { Schema } from "@formily/json-schema";
import { usePrefixCls } from "./usePrefixCls";
import { ArrayBase } from "@ey-component/components";
import { plus } from "@ey-utils/utils/numberPrecision";
import { getPathValue } from "@ey-utils/utils/utils";
import { getTempData } from "@ey-utils/utils/services/get-temp-data";

interface ObservableColumnSource {
  field: any;
  fieldProps: any;
  columnProps: ColumnProps<any>;
  schema: Schema;
  display: any;
  name: string;
}
interface IArrayTablePaginationProps extends PaginationProps {
  dataSource?: any[];
  children?: (
      dataSource: any[],
      pagination: React.ReactNode,
      oldDataSource: any[]
  ) => React.ReactElement;
  onChange?: (num: number) => void;
  filterSearchFlag?: number;
}

interface IStatusSelectProps extends SelectProps<any> {
  pageSize?: number;
}

type ComposedArrayTable = React.FC<TableProps<any>> &
    any & {
  Column?: React.FC<ColumnProps<any>>;
  disabledIndex?: number[];
};

const isColumnComponent = (schema: Schema) => {
  return schema["x-component"]?.indexOf("Column") > -1;
};

const isOperationsComponent = (schema: Schema) => {
  return schema["x-component"]?.indexOf("Operations") > -1;
};

const isAdditionComponent = (schema: Schema) => {
  return [
    "ArrayTable.Export",
    "ArrayTable.Upload",
    "ArrayTable.DownOut",
    "ArrayTable.Addition",
    "ArrayTable.SalerWtdwl",
    "ArrayTable.Delete",
    "ArrayTable.Copy",
    "ArrayTable.OpenLib",
    "ArrayTable.BatchCopy",
  ].includes(schema["x-component"]);
};

const useArrayTableSources = () => {
  const arrayField = useField();
  const schema = useFieldSchema();

  const parseSources = (schema: Schema): ObservableColumnSource[] => {
    if (
        isColumnComponent(schema) ||
        isOperationsComponent(schema) ||
        isAdditionComponent(schema)
    ) {
      if (!schema["x-component-props"]?.["dataIndex"] && !schema["name"])
        return [];
      const name = schema["x-component-props"]?.["dataIndex"] || schema["name"];
      const field = arrayField.query(arrayField.address.concat(name)).take();
      const fieldProps = field?.props || schema.toFieldProps();
      const columnProps =
          field?.component?.[1] || schema["x-component-props"] || {};
      const display = field?.display || schema["x-display"];
      return [
        {
          name,
          display,
          field,
          fieldProps,
          schema,
          columnProps,
        },
      ];
    } else if (schema.properties) {
      return schema.reduceProperties((buf, schema) => {
        return buf.concat(parseSources(schema));
      }, []);
    }
  };

  const parseArrayItems = (schema: Schema["items"]) => {
    const sources: ObservableColumnSource[] = [];
    const items = isArr(schema) ? schema : schema ? [schema] : [];
    return items.reduce((columns, schema) => {
      const item = parseSources(schema);
      if (item) {
        return columns.concat(item);
      }
      return columns;
    }, sources);
  };

  if (!schema) throw new Error("can not found schema object");

  return parseArrayItems(schema.items);
};

const useArrayTableColumns = (
    dataSource: any[],
    sources: ObservableColumnSource[],
    renderCol?: any
): TableProps<any>["columns"] => {
  return sources.reduce(
      (buf, { name, columnProps, schema, display, ...arg }: any, key) => {
        if (display !== "visible") return buf;
        if (!isColumnComponent(schema)) return buf;
        return buf.concat({
          ...columnProps,
          key,
          dataIndex: name,
          render: (value: any, record: any) => {
            const index = dataSource.indexOf(record);
            if (!columnProps?.editable) {
              if (columnProps?.columnRenderType || name === "operations") {
                const childrens = (
                    <ArrayBase.Item index={index}>
                      <RecursionField
                          schema={schema}
                          name={index}
                          onlyRenderProperties
                      />
                    </ArrayBase.Item>
                );
                return childrens;
              }

              return <div>{record[columnProps?.colName] || value}</div>;
            } else {
              if (renderCol) {
                const newSchema = renderCol({ record, value, schema, index });
                const children = (
                    <ArrayBase.Item index={index}>
                      <RecursionField
                          schema={newSchema}
                          name={index}
                          onlyRenderProperties
                      />
                    </ArrayBase.Item>
                );
                return children;
              } else {
                const children = (
                    <ArrayBase.Item index={index}>
                      <RecursionField
                          schema={schema}
                          name={index}
                          onlyRenderProperties
                      />
                    </ArrayBase.Item>
                );
                return children;
              }
            }
          },
        });
      },
      []
  );
};

export const useAddition = () => {
  const schema = useFieldSchema();
  return schema.reduceProperties((addition, schema) => {
    if (isAdditionComponent(schema)) {
      return addition.concat(
          (<RecursionField schema={schema} name={schema.name} />) as any
      );
    }
    return addition;
  }, []);
};

const StatusSelect: React.FC<IStatusSelectProps> = observer((props) => {
  const form = useForm();
  const field = useField<any>();
  const prefixCls = usePrefixCls("formily-array-table");
  const errors = form.queryFeedbacks({
    type: "error",
    address: `${field.address}.*`,
  });
  const createIndexPattern = (page: number) => {
    const pattern = `${field.address}.*[${(page - 1) * props.pageSize}:${
        page * props.pageSize
    }].*`;
    return FormPath.parse(pattern);
  };
  const options = props.options?.map(({ label, value }) => {
    const hasError = errors.some(({ address }) => {
      return createIndexPattern(value).match(address);
    });
    return {
      label: hasError ? <Badge dot>{label}</Badge> : label,
      value,
    };
  });

  const width = String(options?.length).length * 15;

  return (
      <Select
          value={props.value}
          onChange={props.onChange}
          options={options}
          virtual
          style={{
            width: width < 60 ? 60 : width,
          }}
          className={cls(`${prefixCls}-status-select`, {
            "has-error": errors?.length,
          })}
      />
  );
});

const ArrayTablePagination: React.FC<IArrayTablePaginationProps> = (props) => {
  const [current, setCurrent] = useState(1);
  const prefixCls = usePrefixCls("formily-array-table");
  const pageSize = props.pageSize || 5;
  const size = props.size || "default";
  const dataSource = props.dataSource || [];
  const startIndex = (current - 1) * pageSize;
  const endIndex = startIndex + pageSize - 1;
  const total = dataSource?.length || 0;
  const totalPage = Math.ceil(total / pageSize);
  const needPagination = props.needPagination;
  const { filterSearchFlag } = props || {};

  const pages = Array.from(new Array(totalPage)).map((_, index) => {
    const page = index + 1;
    return {
      label: page,
      value: page,
    };
  });
  const handleChange = (current: number) => {
    setCurrent(current);
    props?.onChange(current);
  };

  useEffect(() => {
    if (filterSearchFlag && filterSearchFlag > 0) {
      setCurrent(1);
    }
  }, [filterSearchFlag]);

  const renderPagination = () => {
    if (totalPage <= 1) return;
    return (
        <div className={`${prefixCls}-pagination`}>
          <Space>
            <StatusSelect
                value={current}
                pageSize={pageSize}
                onChange={handleChange}
                options={pages}
                notFoundContent={false}
            />
            <Pagination
                {...props}
                pageSize={pageSize}
                current={current}
                total={dataSource.length}
                size={size}
                showSizeChanger={false}
                onChange={handleChange}
            />
          </Space>
        </div>
    );
  };

  return (
      <Fragment>
        {needPagination
            ? props.children?.(
                dataSource?.slice(startIndex, endIndex + 1),
                renderPagination(),
                dataSource
            )
            : props.children(dataSource, null, dataSource)}
      </Fragment>
  );
};
// 计算汇总值
// 计算汇总值
const FooterCalculate: React.FC<any> = (props: {
  calculateCol: Array<any>;
  dataSource: any;
  columns: any;
  expression?: any;
  oldDataSource?: any[];
  calType: "ALL" | "PAGE"; // 分页计算还是总体，默认分页
  simpleCalculateCol?: [{key: string; name: string}],
}) => {
  const [calTota, setCalTota] = useState<any>();
  const dataSource = props.dataSource;
  const columns = props.columns;
  const form = useForm();
  const arrayField = useField<any>();
  const { calType = "PAGE", oldDataSource } = props;

  useEffect(() => {
    // 当计算可编辑值的时候动态设置
    if (columns.length > 0) {
      form.addEffects(`${arrayField.address}.*`, () => {
        for (let i = 0; i < props.calculateCol.length; i++) {
          const isString = props.calculateCol[i] === "string";
          const targetKey = isString
              ? props.calculateCol[i]
              : props.calculateCol[i].targetKey;
          const depKey = !isString ? props.calculateCol[i].depKey : undefined;
          onFieldInputValueChange(
              `${arrayField.address}.*.${targetKey}`,
              () => {
                const da = arrayField.value || [];
                calTotal(da, columns);
              }
          );
          depKey &&
          onFieldValueChange(`${arrayField.address}.*.${depKey}`, () => {
            const da = arrayField.value || [];
            calTotal(da, columns);
          });
        }
      });
    }
    return () => {
      // 当计算可编辑值的时候动态设置
      if (columns.length > 0) {
        form.removeEffects(`${arrayField.address}.*`);
      }
    };
  }, [columns]);

  // 获取列名
  const filterCol = (columns: any) => {
    const colDisplay: any[] = [];

    props?.calculateCol.map((item) => {
      let isString = typeof item === "string";
      const targetKey = isString ? item : item.targetKey;
      const column = columns.find((o: any) => o.dataIndex === targetKey);

      if (column) {
        colDisplay.push({
          name: column.dataIndex,
          val: [],
          displayName: isString
              ? localStorage.getItem("umi_locale") === "zh_CN"
                  ? `${column.title}汇总`
                  : `${column.title}`
              : // : `${column.title}total`
              item.displayName,
          condition: !isString ? item.condition : undefined,
        });
      }
    });

    return colDisplay;
  };
  // 通过列名获取值并汇总
  useEffect(() => {
    const ds = calType === "ALL" ? oldDataSource : dataSource;
    calTotal(ds || [], columns);
  }, [dataSource, oldDataSource, calType]);
  const calTotal = (dataSource: any, columns: any) => {
    const t = new Date().getTime();
    if(props.simpleCalculateCol) {
      const col = [];
      dataSource.forEach(item => {
        props.simpleCalculateCol.forEach((colKeyItem, index) => {
          const {key, name} = colKeyItem;
          const colItem = { displayName: name, total: col[index]?.total || '0' };
          colItem.total = plus(colItem.total, item[key] || '0');
          col[index] = colItem;
        })
      })
      setCalTota(col);
      console.log(new Date().getTime() - t, 'simpleCalculateCol')
      return;
    }
    
    let col: any = filterCol(columns) || [];
    for (let i = 0; i < col?.length; i++) {
      if (col.condition && col.condition?.indexOf("$self") < 0) {
        return console.error('当前表达式不符合规则"`${$self.xx > 0}`"');
      }
      col[i].val = dataSource?.map((a: any) => {
        var $self = {};

        for (let key in a) {
          $self[key] = a[key];
        }

        if (col[i].condition) {
          return eval(col[i].condition) ? a[col[i].name] || "0" : "0";
        }

        return a[col[i].name] || "0";
      });
    }
    for (let i = 0; i < col?.length; i++) {
      const val1 = col[i].val.shift() || "0";
      const val2 = col[i].val.shift() || "0";
      col[i].total = plus(val1, val2, ...(col[i].val || []));
    }
    setCalTota(col);
    console.log(new Date().getTime() - t, 'simpleCalculateCol')
  };
  // 设置表达式动态计算值
  const expressionfunc = () => {
    const displayName =
        localStorage.getItem("umi_locale") === "zh_CN" ? `汇总` : ``;
    // localStorage.getItem("umi_locale") === "zh_CN" ? `汇总` : `total`;
    let total = 0;
    // 校验当前表达式是否包含$self
    if (props.expression?.indexOf("$self") < 0) {
      console.error('当前表达式不符合规则"`${$self.xx-$self.aa}`"');
    } else {
      var $self = {};
      for (let i = 0; i < calTota.length; i++) {
        $self[calTota[i].name] = calTota[i].total;
      }
      if (props.expression) {
        total = eval(props.expression);
      }
      return (
          <>
            <span>{displayName}:</span>
            <span>{total}</span>
          </>
      );
    }
  };
  return (
      <Space size="large">
        {props.expression ? expressionfunc() : null}

        {calTota?.map((i: any) => {
          return (
              <span key={i.displayName}>
            <span>{i.displayName} : </span>
            <strong>{i.total}</strong>
          </span>
          );
        })}
      </Space>
  );
};
// 设置表达式 calculate
export const ArrayTable: ComposedArrayTable = observer(
    (
        props: TableProps<any> & {
          onValueChange?: () => void;
          disabledIndex?: number[];
          nNeedAddition?: boolean;
          calculateCol?: any[];
          expression?: string;
          rowKey?: string;
          remoteDataPath?: string | string[];
          filterFn?: (item: any, index: number) => boolean;
          calType?: "ALL" | "PAGE";
          isRowKey?: boolean;
          selectionInit?: boolean;
          simpleCalculateCol?: [{key: string; name: string}]
        }
    ) => {
      const ref = useRef<HTMLDivElement>();
      const field = useField<any>();
      const prefixCls = usePrefixCls("formily-array-table");

      const dataSource = Array.isArray(field.value) ? field.value.slice() : [];
      const [selectedRowKeys, setSelectedRowKeys] = useState([]);
      const sources = useArrayTableSources();
      const {
        calculateCol,
        expression,
        disabledIndex,
        rowKey,
        remoteDataPath,
        filterFn,
        filterSearchFlag,
        renderCol,
        selectionInit,
        simpleCalculateCol
      } = props || {};

      const columns = useArrayTableColumns(dataSource, sources, renderCol);

      const pagination = isBool(props.pagination) ? {} : props.pagination;
      const needPagination = props?.needPagination || false;
      let scroll = { x: "max-content" };
      useEffect(() => {
        if (selectionInit && dataSource?.length) {
          setSelectedRowKeys(dataSource?.filter(it => it.selections).map(it => {
            return it?.[rowKey];
          }) || []);
        }
      }, []);
      if (props?.scrollConfig && typeof props?.scrollConfig === "object") {
        scroll = {
          ...scroll,
          ...props?.scrollConfig,
        };
      }
      const addition = props?.nNeedAddition ? <></> : useAddition();

      const defaultRowKey = (record: any, index: any) => {
        return record[rowKey] || index;
      };
      const addTdStyles = (node: HTMLElement) => {
        const helper = document.body.querySelector(`.${prefixCls}-sort-helper`);
        if (helper) {
          const tds = node.querySelectorAll("td");
          requestAnimationFrame(() => {
            helper.querySelectorAll("td").forEach((td, index) => {
              if (tds[index]) {
                td.style.width = getComputedStyle(tds[index]).width;
              }
            });
          });
        }
      };
      const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
        setSelectedRowKeys(newSelectedRowKeys);
        const val = dataSource?.map((i: any, index: any) => {
          return {
            ...i,
            selections: newSelectedRowKeys.includes(i[rowKey] || index),
          };
        });
        field.value = val;
        props?.selectNumber?.(newSelectedRowKeys);
        props?.rowSelection?.onChange(newSelectedRowKeys);
      };
      useEffect(() => {
        const dd = dataSource?.filter?.((i: any) => i?.selections);
        if (dd?.length) {
          // fix: 默认值
          const selectedRowKeys = dd?.map?.((a: any) => a[rowKey]);
          setSelectedRowKeys(selectedRowKeys);
        }
      }, [JSON.stringify(dataSource?.length)]);
      // 默认产出一个选中清空
      useEffect(() => {
        if (props) {
          props?.clearnSelect?.(setSelectedRowKeys);
        }
      }, [props?.clearnSelect]);

      const rowSelection: any = {
        selectedRowKeys,
        onChange: onSelectChange,
      };
      const [loading, setLoading] = useState<any>(false);
      const api: any = props.api;
      useEffect(() => {
        if (api?.url) {
          setLoading(true);
          getTempData(api)
              .then((res: any) => {
                field.value = getPathValue(res, remoteDataPath) || [];
                setLoading(false);
              })
              .catch((err) => {
                field.value = [];
                setLoading(false);
              });
        }
      }, [JSON.stringify(api)]);
      const TdCell = (props: any) => {
        // onMouseEnter, onMouseLeave在数据量多的时候，会严重阻塞表格单元格渲染，严重影响性能
        const { onMouseEnter, onMouseLeave, ...restProps } = props;
        return <td {...restProps} />;
      };
      return (
          <ArrayTablePagination
              {...pagination}
              needPagination={needPagination}
              filterSearchFlag={filterSearchFlag}
              dataSource={filterFn ? dataSource?.filter(filterFn) : dataSource}
          >
            {(dataSource, pager, oldDataSource) => (
                <div ref={ref} className={prefixCls}>
                  <ArrayBase
                      onValueChange={props?.onValueChange}
                      disabledIndex={disabledIndex}
                      isRowKey={props?.isRowKey}
                  >
                    {addition}
                    <Table
                        size="small"
                        {...props}
                        rowClassName={"ac-array"}
                        rowKey={defaultRowKey}
                        onChange={() => {}}
                        bordered
                        components={
                          needPagination
                              ? {
                                body: { cell: TdCell },
                              }
                              : null
                        }
                        pagination={!simpleCalculateCol && needPagination}
                        rowSelection={props?.needRowSelection ? rowSelection : null}
                        columns={columns}
                        dataSource={
                          filterFn ? dataSource?.filter(filterFn) : dataSource
                        }
                        scroll={scroll}
                        loading={loading}
                    />
                    {calculateCol || expression ? (
                        <FooterCalculate
                            calculateCol={calculateCol}
                            dataSource={dataSource}
                            oldDataSource={oldDataSource}
                            columns={columns}
                            expression={expression}
                            calType={props.calType}
                            simpleCalculateCol={simpleCalculateCol}
                        />
                    ) : null}

                    <div style={{ marginTop: 5, marginBottom: 5 }}>{pager}</div>
                    {sources.map((column, key) => {
                      //专门用来承接对Column的状态管理
                      if (!isColumnComponent(column.schema)) return;
                      return React.createElement(RecursionField, {
                        name: column.name,
                        schema: column.schema,
                        onlyRenderSelf: true,
                        key,
                      });
                    })}
                  </ArrayBase>
                </div>
            )}
          </ArrayTablePagination>
      );
    }
);

ArrayTable.displayName = "ArrayTable";

ArrayTable.Column = () => {
  return <Fragment />;
};

ArrayBase.mixin(ArrayTable);

export default ArrayTable;
